package com.abgame.test.redis.utils.lock;

import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Time;
import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁帮助类RedissonAutoConfiguration
 *
 */
@Component
public class RedissonLockUtils {
    private final static Logger log = LoggerFactory.getLogger(RedissonLockUtils.class);

    private static RedissonClient redissonClient;
    private static final int DEFAULT_WAIT_TIME = 5;
    private static final int DEFAULT_LEASE_TIME = 20;

    @Autowired
    public void setRedissonClient(RedissonClient redissonClient) {
        RedissonLockUtils.redissonClient = redissonClient;
    }

    /**
     * 获取锁对象
     *
     * @param lockKey 锁key
     * @return org.redisson.api.RLock
     * @since 2018/08/31 11:48
     */
    public static RLock getLock(String lockKey) {
        return redissonClient.getLock(lockKey);
    }

    /**
     * 加锁
     *
     * @param lockKey
     * @return
     */
    public static RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    /**
     * 带超时的锁
     *
     * @param lockKey
     * @param timeout 超时时间   单位：秒
     */
    public static RLock lock(String lockKey, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
    }

    /**
     * 带超时的锁
     *
     * @param lockKey
     * @param unit    时间单位
     * @param timeout 超时时间
     */
    public static RLock lock(String lockKey, TimeUnit unit, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    /**
     * 尝试获取锁
     * 最多等待时间(默认) 上锁后自动释放锁时间（默认）
     * @param lockKey   锁key
     * @return true=获取锁成功 false=未获取到锁
     */
    public static boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(DEFAULT_WAIT_TIME, DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("tryLockSeconds Exception lockKey:{}, waitTime:{}, leaseTime:{}", lockKey, DEFAULT_WAIT_TIME, DEFAULT_LEASE_TIME, e);
            return false;
        }
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey   锁key
     * @param unit      时间单位
     * @param waitTime  最多等待时间 （若没有获取到锁，会等待，若在等待时间内获取到锁，则返回true，若等待时间内未获取到锁，则返回false）
     * @param leaseTime 上锁后自动释放锁时间（不管业务是否执行完）
     * @return true=获取锁成功 false=未获取到锁
     */
    public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            log.error("tryLockSeconds Exception lockKey:{}, waitTime:{}, leaseTime:{}", lockKey, waitTime, leaseTime, e);
            return false;
        }
    }

    /**
     * 尝试获取锁, 时间单位秒
     *
     * @param lockKey   锁key
     * @param waitTime  最多等待时间 （若没有获取到锁，会等待，若在等待时间内获取到锁，则返回true，若等待时间内未获取到锁，则返回false）
     * @param leaseTime 上锁后自动释放锁时间（不管业务是否执行完）
     * @return true=获取锁成功 false=未获取到锁
     */
    public static boolean tryLockSeconds(String lockKey, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("tryLockSeconds InterruptedException lockKey:{}, waitTime:{}, leaseTime:{}", lockKey, waitTime, leaseTime, e);
            return false;
        } catch (Exception e) {
            log.error("tryLockSeconds Exception lockKey:{}, waitTime:{}, leaseTime:{}", lockKey, waitTime, leaseTime, e);
            return false;
        }
    }

    /**
     * 尝试获取锁, 时间单位秒
     *
     * @param lockKey 锁key
     * @return true=获取锁成功 false=未获取到锁
     */
    public static boolean tryDefaultLockSeconds(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(DEFAULT_WAIT_TIME, DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 不可重复调用锁
     *
     * @param lockKey      锁key
     * @param unit         时间单位
     * @param unRepeatTime 不可重复调用时间
     * @return boolean true=获取锁 false=未获取锁
     * @since 2018/09/12 16:11
     */
    public static boolean tryRepeatLock(String lockKey, TimeUnit unit, int unRepeatTime) {
        RBucket<Object> bucket = redissonClient.getBucket(lockKey);
        if (bucket.trySet(new Object(), unRepeatTime, unit)) {
            return true;
        }
        return false;
    }

    /**
     * 尝试获取锁
     *
     * @param lock      锁对象
     * @param unit      时间单位
     * @param waitTime  等待时间
     * @param leaseTime 释放时间
     * @return boolean true 获取成功
     * @since 2018/08/31 11:49
     */
    public static boolean tryLock(RLock lock, TimeUnit unit, int waitTime, int leaseTime) {
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }

    /**
     * 尝试获取锁
     *
     * @param lock     锁对象
     * @param unit     时间单位
     * @param waitTime 等待时间
     * @return boolean
     * @since 2018/08/31 11:49
     */
    public static boolean tryLock(RLock lock, TimeUnit unit, int waitTime) {
        try {
            return lock.tryLock(waitTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }

    /**
     * 释放锁
     *
     * @param lockKey
     */
    public static void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        if (lock != null && lock.isLocked()) {
            lock.unlock();
        }
    }

    /**
     * 释放锁
     *
     * @param lock
     */
    public static void unlock(RLock lock) {
        lock.unlock();
    }

}